/**
 *   ___________   __
 *  /_  __/  _/ | / /
 *   / /  / //  |/ / 
 *  / / _/ // /|  /  
 * /_/ /___/_/ |_/ 
 * 
 * C++ library of the Triangular Irregular Network (TIN)
 *
 * Copyright (c) 2021-2031 Yi Zhang (zhangyiss@icloud.com)
 * All rights reserved.
 *
 */

#ifndef _TIN_DELAUNAY_H
#define _TIN_DELAUNAY_H
#include "cmath"
#include "vector"
#include "algorithm"

#define ZERO 1e-5

// Start vertex definition
struct vertex2dc
{
	unsigned int id; // index of the vertex
	double x, y; // position of the vertex
	double elev; // elevation at the vertex

	vertex2dc();

	/**
	 * @brief Construct a new vertex2dc object
	 * 
	 * @param inx      initial x coordinate
	 * @param iny      initial y coordinate
	 * @param inelev   initial elevation
	 * @param inid     initial index
	 */
	vertex2dc(double inx, double iny, double inelev, unsigned int inid);

	/**
	 * @brief Set a vertex2dc object
	 * 
	 * @param inx      initial x coordinate
	 * @param iny      initial y coordinate
	 * @param inelev   initial elevation
	 * @param inid     initial index
	 */
	void set(double inx, double iny, double inelev, unsigned int inid);
};

/**
 * @brief Compare two vertexes. Index of the vertexes are not used for comparsion
 * 
 * @param a       vertex a
 * @param b       vertex b
 * @return true   the two vertexes are at the same location
 * @return false  the two vertexes are not at the same location
 */
bool operator ==(const vertex2dc &a, const vertex2dc &b);

/**
 * @brief Test if the three points are on the same line
 * 
 * @param a_ptr   pointer of the vertex a
 * @param b_ptr   pointer of the vertex b
 * @param c_ptr   pointer of the vertex c
 * @return true   the three vertexes are on the same line
 * @return false  the three vertexes are not on the same line
 */
bool is_collinear(vertex2dc *a_ptr, vertex2dc *b_ptr, vertex2dc *c_ptr);

/**
 * @brief Calculate the circumcircle from three points
 * 
 * @param v0      pointer of the vertex v0
 * @param v1      pointer of the vertex v1
 * @param v2      pointer of the vertex v2
 * @param cx      x coordinate of the returned circumcircle
 * @param cy      y coordinate of the returned circumcircle
 * @param cr      squared radius of the returned circumcircle
 */
void circumcircle(vertex2dc *v0, vertex2dc *v1, vertex2dc *v2, double &cx, double &cy, double &cr);
// End vertex definition

// Start DEM definition
struct triangle;

struct dem_point
{
	double x, y; // position of the DEM location
	double elev; // elevation at the DEM location
	double err; // error of the TIN with respect to the elevation
	triangle *host; // pointer of the triangle that the dem_point falls inside of

	dem_point();

	/**
	 * @brief Construct a new DEM point object
	 * 
	 * @param inx      initial x coordinate
	 * @param iny      initial y coordinate
	 * @param inelev   initial elevation
	 */
	dem_point(double inx, double iny, double inelev);

	/**
	 * @brief Set a DEM point object
	 * 
	 * @param inx      initial x coordinate
	 * @param iny      initial y coordinate
	 * @param inelev   initial elevation
	 */
	void set(double inx, double iny, double inelev);
};
// End DEM definition

/* Start triangle definition
 *            v2
 *            /\
 *          /   \
 *     n2 /      \ n1
 *      /         \
 *    /------------\
 *   v0     n0      v1
 */
struct triangle
{
	int id;
	vertex2dc *vert[3]; // vertex of the triangle
	triangle *neigh[3]; // neighbors of the triangle
	double cx, cy; // center of the triangle's circumcircle
	double cr; // radius of the circumcircle
	std::vector<dem_point*> hosted_dem;

	triangle();

	/**
	 * @brief Construct a new triangle object
	 * 
	 * @param v0ptr   pointer of the vertex 0
	 * @param v1ptr   pointer of the vertex 1
	 * @param v2ptr   pointer of the vertex 2
	 */
	triangle(vertex2dc *v0ptr, vertex2dc *v1ptr, vertex2dc *v2ptr);

	/**
	 * @brief Set a triangle object
	 * 
	 * @param v0ptr   pointer of the vertex 0
	 * @param v1ptr   pointer of the vertex 1
	 * @param v2ptr   pointer of the vertex 2
	 */
	void set(vertex2dc *v0ptr, vertex2dc *v1ptr, vertex2dc *v2ptr);

	/**
	 * @brief Set neighbors of a triangle object
	 * 
	 * @param n0ptr   pointer of the neighboring vertex 0
	 * @param n1ptr   pointer of the neighboring vertex 1
	 * @param n2ptr   pointer of the neighboring vertex 2
	 */
	void set_neighbor(triangle *n0ptr, triangle *n1ptr, triangle *n2ptr);

	/**
	 * @brief Test if the location is inside the triangle
	 * 
	 * @param inx    x coordinate of the input location
	 * @param iny    y coordinate of the input location
	 * @return true   the input location is inside the triangle
	 * @return false  the input location is not inside the triangle
	 */
	bool bound_location(double inx, double iny);

	/**
	 * @brief Interpolate the elevation of the given location inside the triangle
	 * 
	 * @param inx    x coordinate of the input location
	 * @param iny    y coordinate of the input location
	 * @return double  the interpolated elevation at the input location
	 */
	double interpolate(double inx, double iny);
};
// End triangle definition

/**
 * @brief      Generate the TIN from a dense DEM grid
 *
 * @param[in]  dem          Input DEM grid (Ordered from lower left corner to the upper right corner)
 * @param[in]  xmin         The minimal coordinate of the DEM grid on the x-axis
 * @param[in]  xmax         The maximal coordinate of the DEM grid on the x-axis
 * @param[in]  ymin         The minimal coordinate of the DEM grid on the y-axis
 * @param[in]  ymax         The maximal coordinate of the DEM grid on the y-axis
 * @param[in]  dx           Data spacing of the DEM grid on the x-axis
 * @param[in]  dy           Data spacing of the DEM grid on the y-axis
 * @param      out_verts    The output vector of vertex's pointers. The user need to destroy the memories allocated by the function before destroy the vector
 * @param      out_tris     The output vector of triangle's pointers. The user need to destroy the memories allocated by the function before destroy the vector
 * @param[in]  maxi_err     Threshold to quit the algorithm. The default is 1e-0
 * @param[in]  outline_poly If this pointer is not NULL, Cut triangle outside of or intersected with the polygon.
 * @param[in]  err_records  If this pointer is not NULL, record maximal error values after each insertion of vertex.
 */
void grd2tin(const std::vector<double> &dem, double xmin, double xmax, double ymin, double ymax, 
	double dx, double dy, std::vector<vertex2dc*> &out_verts, std::vector<triangle*> &out_tris, 
	double maxi_err = 1e-0, std::vector<vertex2dc> *outline_poly = nullptr, std::vector<double> *err_records = nullptr);

/**
 * @brief      Generate the TIN from random DEM points
 * 
 * @param      dem          Input DEM points
 * @param      out_verts    The output vector of vertex's pointers. The user need to destroy the memories allocated by the function before destroy the vector
 * @param      out_tris     The output vector of triangle's pointers. The user need to destroy the memories allocated by the function before destroy the vector
 * @param[in]  maxi_err     Threshold to quit the algorithm. The default is 1e-0
 * @param[in]  outline_poly If this pointer is not NULL, Cut triangle outside of or intersected with the polygon.
 * @param[in]  err_records  If this pointer is not NULL, record maximal error values after each insertion of vertex.
 */
void rnd2tin(const std::vector<dem_point> &dem, std::vector<vertex2dc*> &out_verts, 
	std::vector<triangle*> &out_tris, double maxi_err = 1e-0, std::vector<vertex2dc> *outline_poly = nullptr, 
	std::vector<double> *err_records = nullptr);

#endif // _TIN_DELAUNAY_H